#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
/*
 * Copyright © 2014, Finium Sdn Bhd, All Rights Reserved
 * 
 * AliasToBeanNestedResultTransformer.java
 * Modification History
 * *************************************************************
 * Date				Author						Comment
 * Oct 25, 2013		Venkaiah Chowdary Koneru	Created
 * *************************************************************
 */
package ${package}.support.hibernate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;

/**
 * Hibernate enhanced AliasToBeanResultTransformer that supports projecting
 * one-to-one and many-to-one associations.
 * 
 * referred from <br>
 * https://github.com/samiandoni/AliasToBeanNestedResultTransformer
 * /blob/master/AliasToBeanNestedResultTransformer.java
 * 
 * @author Venkaiah Chowdary Koneru
 */
public class AliasToBeanNestedResultTransformer extends
        AliasedTupleSubsetResultTransformer
{
    private static final long serialVersionUID = 1L;

    private final Class<?> resultClass;

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isTransformedValueATupleElement(String[] aliases,
            int tupleLength)
    {
        return false;
    }

    /**
     * @param resultClass
     *            Transformer class
     */
    public AliasToBeanNestedResultTransformer(Class<?> resultClass)
    {
        this.resultClass = resultClass;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object transformTuple(Object[] tuple, String[] aliases)
    {

        Map<Class<?>, List<?>> subclassToAlias = new HashMap<Class<?>, List<?>>();
        List<String> nestedAliases = new ArrayList<String>();

        try
        {
            for (int i = 0; i < aliases.length; i++)
            {
                String alias = aliases[i];
                if (alias.contains("."))
                {
                    nestedAliases.add(alias);

                    String[] sp = alias.split("${symbol_escape}${symbol_escape}.");
                    String fieldName = sp[0];
                    String aliasName = sp[1];

                    Class<?> subclass = resultClass.getDeclaredField(fieldName)
                            .getType();

                    if (!subclassToAlias.containsKey(subclass))
                    {
                        List<Object> list = new ArrayList<Object>();
                        list.add(new ArrayList<Object>());
                        list.add(new ArrayList<String>());
                        list.add(fieldName);
                        subclassToAlias.put(subclass, list);
                    }
                    ((List<Object>) subclassToAlias.get(subclass).get(0))
                            .add(tuple[i]);
                    ((List<String>) subclassToAlias.get(subclass).get(1))
                            .add(aliasName);
                }
            }
        }
        catch (NoSuchFieldException e)
        {
            throw new HibernateException("Could not instantiate resultclass: "
                    + resultClass.getName(), e);
        }

        Object[] newTuple = new Object[aliases.length - nestedAliases.size()];
        String[] newAliases = new String[aliases.length - nestedAliases.size()];
        int i = 0;
        for (int j = 0; j < aliases.length; j++)
        {
            if (!nestedAliases.contains(aliases[j]))
            {
                newTuple[i] = tuple[j];
                newAliases[i] = aliases[j];
                ++i;
            }
        }

        ResultTransformer rootTransformer = new AliasToBeanResultTransformer(
                resultClass);
        Object root = rootTransformer.transformTuple(newTuple, newAliases);

        for (Class<?> subclass : subclassToAlias.keySet())
        {
            ResultTransformer subclassTransformer = new AliasToBeanResultTransformer(
                    subclass);
            Object subObject = subclassTransformer.transformTuple(
                    ((List<Object>) subclassToAlias.get(subclass).get(0))
                            .toArray(),
                    ((List<Object>) subclassToAlias.get(subclass).get(1))
                            .toArray(new String[0]));

            PropertyAccessor accessor = PropertyAccessorFactory
                    .getPropertyAccessor("property");
            accessor.getSetter(resultClass,
                    (String) subclassToAlias.get(subclass).get(2)).set(root,
                    subObject, null);
        }

        return root;
    }
}
